/**
 * @file   MiraIndexCont.h
 * @author Josep Sabater (jsabaterm@el.ub.edu)
 * @date   July, 2016
 * @version 1.0.0
 * @ingroup MiraGraphUBLib
 * @brief Indexed container
 * This is a constant time access container to store data.
 *
 */

#ifndef MIRA_INDEX_CONT_H
#define MIRA_INDEX_CONT_H

#include <cassert>
#include <cstddef>

#include <vector>

namespace MiraUtilUBLib{
    template <typename T>
          class IndexCont {

            private:
            
            // the swap member function (should never fail!)
            void swap_(IndexCont<T> & other) {
                // swap all the members
                using std::swap;
                swap(vector_, other.vector_);
            }           

          public:
            typedef size_t SizeType_t;
            typedef SizeType_t Key_t;
            typedef T Value_t;
            typedef std::vector<Value_t> Vector_t;
              
            typedef typename Vector_t::reference Reference_t;
            typedef typename Vector_t::const_reference ConstReference_t;
            typedef typename Vector_t::iterator        Iterator_t;
            typedef typename Vector_t::const_iterator  ConstIterator_t;
            typedef std::reverse_iterator<Iterator_t>  ReverseIterator_t;
            typedef std::reverse_iterator<ConstIterator_t> ConstReverseIterator_t;  
            
            /// Default constructor
            IndexCont() : vector_() {}
            
            /// Constructor with specified default value.            
            IndexCont(const int size, const Value_t &value = Value_t()) : vector_(size, value) {}
            IndexCont(const std::vector<Value_t>& vec) : vector_(vec.begin(), vec.end()) {}
            IndexCont(const IndexCont<Value_t> &c) : vector_(c.vector_.begin(), c.vector_.end()) {}            

            /// Returns the size of the map.
            SizeType_t size() const { return vector_.size(); }
            SizeType_t maxSize() const { return vector_.max_size(); }
            void resize(int size, const Value_t &val = T()) { vector_.resize(size, val); }
            bool empty() const { return vector_.empty(); }
            
            void reset() { vector_.clear(); }

            void add(const Value_t &v) { vector_.push_back(v); }
            void remove(const Key_t &k) { isKeyValid(k); vector_.erase(vector_.begin() + k); }
            
            const Vector_t toVector() const { return vector_; }
            
            // iterators:
            Iterator_t       begin()        { return vector_.begin(); }
            ConstIterator_t  begin()  const { return vector_.begin(); }
            ConstIterator_t  cbegin() const { return vector_.cbegin(); }
            Iterator_t       end()          { return vector_.end(); }
            ConstIterator_t  end()    const { return vector_.end(); }
            ConstIterator_t  cend()   const { return vector_.cend(); }
     
            // element access:
            Reference_t operator[](const Key_t &k) { 
                assert( isKeyValid(k) ); return vector_[k]; 
            }

            ConstReference_t operator[](const Key_t &k) const { 
                assert( isKeyValid(k) ); return vector_[k]; 
            }           
                        
            IndexCont<T> & operator = (IndexCont<T> other){ 
                swap_(other); return *this;                    
            }           
                
            bool isKeyValid(const Key_t &k) const { 
                return( (!empty()) && (k>=0) && (k<size()) );
            }                
             
        protected:
              Vector_t vector_;
        };

}
#endif /* MIRA_INDEX_CONT_H */

